package goutil

import (
	"fmt"
	"os"
	"reflect"
	"strings"
)

type ParseResult int

// 命令行参数结构体.
// 支持的类型:
//  bool
//  string
//  []string
// tag说明:
//  longOpt:    长选项名, 格式: --<longOpt>=<args>
//  shortOpt:   短选项名, 格式: -<shortOpt> <args>
//  brief:      简要说明
// help:
//  仅显示usage, 不退出程序
type GoutilArgs struct {
	Help             bool     `longOpt:"help" shortOpt:"h" brief:"show this usage"`
	GoutilConfigPath string   `longOpt:"goutil.config.path" shortOpt:"c" brief:"profile path"`
	GoutilConfigSub  []string `longOpt:"goutil.config.sub" shortOpt:"s" brief:"sub-profile names. .e.g: -s dev,intranet,debug"`
	Result           ParseResult
	// TODO: 使用map[string]string保存参数
}

const (
	longOptFlag  = "--"
	shortOptFlag = "-"
)

// 解析结果
const (
	_            ParseResult = iota
	ParseSuccess             // 解析通过
	ParseNoArg               // 选项不需要参数
	ParseFailed              // 解析失败

)

// 打印Usage
func showUsage(vals reflect.Value, tags reflect.Type) {
	var tagName = []string{"shortOpt", "longOpt", "brief"}
	count := vals.NumField()
	out := make([][]string, count)
	var maxLen [3]int
	for i := 0; i < count; i++ {
		tagNum := len(tagName)
		out[i] = make([]string, tagNum)
		t := tags.Field(i).Tag
		for j := 0; j < tagNum; j++ {
			out[i][j] = t.Get(tagName[j])
			if maxLen[j] < len(out[i][j]) {
				maxLen[j] = len(out[i][j])
			}
		}
	}
	println("\nUsage:", os.Args[0], "[options]")
	for i := 0; i < count; i++ {
		if out[i][0] == "" && out[i][1] == "" {
			continue
		}
		println(fmt.Sprintf("  -%-*s, --%-*s   %-*s",
			maxLen[0], out[i][0], maxLen[1], out[i][1], maxLen[2], out[i][2]))
	}
}

// 解析并设置参数
func setValue(value reflect.Value, opt, arg string) ParseResult {
	if arg == "" && value.Kind() != reflect.Bool {
		println("Option needs an argument:", "'"+opt+"'")
		return ParseFailed
	}
	switch value.Kind() {
	case reflect.String:
		value.SetString(arg)
	case reflect.Bool:
		value.SetBool(true)
		return ParseNoArg
	case reflect.Slice:
		seg := strings.Split(arg, ",")
		for _, s := range seg {
			switch value.Type().Elem().Kind() {
			case reflect.String:
				value.Set(reflect.Append(value, reflect.ValueOf(s)))
			default:
				panic(fmt.Errorf("Unsupport slice element type: %s\n",
					value.Type().Elem().Kind().String()))
			}
		}
	default:
		panic(fmt.Errorf("Unsupport type: %s\n", value.Type().String()))
	}
	return ParseSuccess
}

// 获取命令行参数
func NewArgs(visible bool) *GoutilArgs {
	args := GoutilArgs{}
	vals := reflect.ValueOf(&args).Elem()
	tags := reflect.TypeOf(&args).Elem()
	count := vals.NumField()
	for j := 0; j < len(os.Args); j++ {
		option := os.Args[j]
		matched := false
		for i := 0; i < count; i++ {
			arg := ""
			kv := strings.Split(option, "=")
			if kv[0] == longOptFlag+tags.Field(i).Tag.Get("longOpt") {
				option = kv[0]
				if len(kv) > 1 {
					arg = strings.Join(kv[1:], "")
				}
				matched = true
			} else if option == shortOptFlag+tags.Field(i).Tag.Get("shortOpt") {
				j++
				if j < len(os.Args) {
					arg = os.Args[j]
				}
				matched = true
			}
			if matched {
				args.Result = setValue(vals.Field(i), option, arg)
				if args.Result == ParseNoArg {
					j--
				}
				break
			}
		}
		if args.Result == ParseFailed {
			break
		}
	}
	if visible && (args.Result == ParseFailed || args.Help) {
		showUsage(vals, tags)
	}

	return &args
}
